# Copyright (C) 2008 Robin Vobruba <hoijui.quaero@gmail.com>

# use ../SConstruct to build spring.
# even if you want to build only subparts of it,
# that are possibly defined in this file.

""" Available targets.
Each target has an equivalent install target. E.g. `NullAI' has
`install-NullAI' and the default target has `install'.

[default]
AIInterfaces
	C
	Java
SkirmishAI
	RAI
	NTAI
	KAI
	KAIK
	AAI
	JCAI
	NullAI
	NullLegacyCppAI
	NullJavaAI
	NullOOJavaAI
"""


Import(['env', 'ai_env', 'streflop_lib'])

import os, sys
sys.path.append('rts/build/scons')
import filelist

# the next four lines are needed when compiling on windows
# (seems to be a SCons bug)
import SCons.Tool.javac
import SCons.Tool.jar
SCons.Tool.javac.generate(ai_env)
SCons.Tool.jar.generate(ai_env)


# Creates an environment for a sub-dir of the dir where env_old is used
# eg.: we are in AI/ and want an env for AI/Skirmish/,
# then the subPart is Skirmish.
def create_sub_interface(env_old, subPart):
	return env_old.Clone(
			# once we have many SConscript files (one for each sub-dir)
			# we will not need the sourcedir anymore,
			# as it will be equal to CWD
			sourcedir = os.path.join(env_old['sourcedir'], subPart),
			builddir = os.path.join(env_old['builddir'], subPart),
			installprefix = os.path.join(env_old['installprefix'], subPart)
			)

# stores shared objects so newer scons versions don't choke with
def create_shared_objects(env, fileList, suffix, additionalCPPDEFINES = []):
	objsList = []
	myEnv = env.Clone()
	myEnv.AppendUnique(CPPDEFINES = additionalCPPDEFINES)
	for f in fileList:
		while isinstance(f, list):
			f = f[0]
		fpath, fbase = os.path.split(f)
		fname, fext = fbase.rsplit('.', 1)
		objsList.append(myEnv.SharedObject(os.path.join(fpath, fname + suffix), f))
	return objsList


# retrieves the first line of a text file
def read_first_line(fileName, defaultValue):
	text = defaultValue
	if os.path.exists(fileName):
		file = open(fileName, 'r')
		text = file.readline().strip()
		file.close()
	return text

# retrieves the version of an AI Interface from the following file:
# {spring_source}/AI/Interfaces/${interfaceName}/VERSION
def fetch_aiInterface_version(interfaceName):
	versionFile = os.path.join('Interfaces', interfaceName, 'VERSION')
	return read_first_line(versionFile, 'UNKNOWN_VERSION')

# retrieves the version of a Skirmish AI from the following file:
# {spring_source}/AI/Skirmish/${aiName}/VERSION
def fetch_skirmishAi_version(aiName):
	versionFile = os.path.join('Skirmish', aiName, 'VERSION')
	return read_first_line(versionFile, 'UNKNOWN_VERSION')


# Installs files plus empty directories recursively,
# preserving directory structure.
def install_dir_verbatim(env, dstPath, srcPath, instList):
	if os.path.exists(srcPath):
		files = filelist.list_files_recursive(env, srcPath, exclude_dirs = False, path_relative = True)
		for f in files:
			f_src_file = os.path.join(srcPath, f)
			f_dst_path = os.path.join(dstPath, os.path.split(f)[0])
			f_dst_file = os.path.join(dstPath, f)
			if not (os.path.isdir(f_src_file) and (os.path.exists(f_dst_file) or len(os.listdir(f_src_file)) != 0)):
				instList += [env.Install(f_dst_path, f_src_file)]

# Installs files plus empty directories recursively,
# preserving directory structure.
# dstPath has to be specified relative to env['installprefix']
# and srcPath relative to CWD in the source tree
def install_dir_verbatim_rel(env, dstPath, srcPath, instList):
	dstPath_new = os.path.join(env['installprefix'], dstPath)
	return install_verbatim(env, dstPath_new, srcPath, instList)


def ensureDirExists(toCreateDir):
	if not os.path.exists(toCreateDir):
		try:
			os.makedirs(toCreateDir)
		except os.error:
			sys.stderr.write('failed creating dir ' + toCreateDir + "\n")
ensureDirExists_action = SCons.Action.ActionFactory(ensureDirExists, lambda toCreateDir: 'Creating dir "%s"' % toCreateDir)

			
class AWKExecutor:
	instances = {}
	awk_bin = 'awk'
	if sys.platform == 'win32':
		if not env['mingwlibsdir']:
			awk_bin = os.path.abspath(os.path.join('mingwlibs', 'bin', 'awk.exe'))
		else:
			awk_bin = os.path.abspath(os.path.join(env['mingwlibsdir'], 'bin', 'awk.exe'))
		# try ../mingwlibsdir because of chdir()
		if not os.path.isfile(awk_bin):
			awk_bin = os.path.abspath(os.path.join('..', env['mingwlibsdir'], 'bin', 'awk.exe'))
		if not os.path.isfile(awk_bin):
			# try msys default
			print awk_bin, 'not found, trying gawk.exe'
			awk_bin = 'gawk.exe'

	myEnv = None
	myName = []
	myScripts = []
	myVars = []
	myInputFile = None
	myOutput = []
	myWorkDir = None

	def __init__(self, env, name, awkScripts, awkVars=[], inputFile = None, output = [], workDir = ''):
		AWKExecutor.instances[name] = self
		self.myEnv = env
		self.myName = name
		self.myScripts = awkScripts
		self.myVars = awkVars
		self.myInputFile = inputFile
		self.myOutput = output
		self.myWorkDir = workDir

	def createExecuteCommand(self):
		cmdLine = self.awk_bin
		for v in self.myVars:
			cmdLine += ' -v ' + v
		for s in self.myScripts:
			cmdLine += ' -f ' + s
		#cmdLine += ' $SOURCE'
		if self.myInputFile != None:
			cmdLine += ' ' + self.myInputFile
		executeCommand = self.myEnv.Command(self.myOutput, self.myInputFile, cmdLine, chdir = self.myWorkDir)
		#""" This sets up a dummy target to do the lib globbing etc. at build time """
		#executeCommand = self.myEnv.Command(self.myName + '_execute_dummyTarget', [], cmdLine)
		#env.Alias(self.myName, executeCommand)
		return executeCommand


import shutil
import exceptions
from shutil import copytree
def myCopyTree(src, dst, symlinks=False, ignore=None):
	names = os.listdir(src)
	if ignore is not None:
		ignored_names = shutil.ignore(src, names)
	else:
		ignored_names = set()
	
	if (not os.path.exists(dst)):
		os.makedirs(dst)
	errors = []
	for name in names:
		if name in ignored_names:
			continue
		srcname = os.path.join(src, name)
		dstname = os.path.join(dst, name)
		try:
			if symlinks and os.path.islink(srcname):
				linkto = os.readlink(srcname)
				os.symlink(linkto, dstname)
			elif os.path.isdir(srcname):
				myCopyTree(srcname, dstname, symlinks, ignore)
			else:
				shutil.copy2(srcname, dstname)
				# XXX What about devices, sockets etc.?
		except (IOError, os.error), why:
			errors.append((srcname, dstname, str(why)))
		# catch the Error from the recursive copytree so that we can
		# continue with other files
		except exceptions.Error, err:
			errors.extend(err.args[0])
	try:
		shutil.copystat(src, dst)
	except exceptions.WindowsError:
		# can't copy file access times on Windows
		pass
	except exceptions.OSError, why:
		errors.extend((src, dst, str(why)))
	if errors:
		raise Error, errors



class JavaProject:
	instances = {}

	#env = None
	name = []
	classPathDirs = []
	sourceDirs = []
	outDir = None
	binJarFile = None
	additionalClassPath = None

	def __init__(self, env, name, classPathDirs, sourceDirs, sourceDirs_gen, outDir, binJarFile, srcJarFile, additionalClassPath = None, dependsOnProject = None):
		""" The first entry in sourceDirs will be the main one, which all others will have to depend on.
		Does source file globbing at build time, so source files generated during build time can be used. """
		JavaProject.instances[name] = self
		self.env = env
		self.name = name
		self.classPathDirs = classPathDirs
		self.sourceDirs = sourceDirs
		self.sourceDirs_gen = sourceDirs_gen
		self.outDir = outDir
		self.binJarFile = binJarFile
		self.srcJarFile = srcJarFile
		self.additionalClassPath = additionalClassPath
		self.dependsOnProject = dependsOnProject
		if (len(self.sourceDirs_gen) > 0):
			self.mainSrcDirs = self.sourceDirs_gen
			self.mainSrcDir = self.sourceDirs_gen[0]
		else:
			self.mainSrcDirs = self.sourceDirs
			self.mainSrcDir = self.sourceDirs[0]
		self.doPrepareEnv()
		return

	def createClassPathPart(self, path, prefixPath = '', absolute = False):
		jarList = filelist.list_files_recursive(self.env, path, exclude_dirs = True, path_relative = True, include_regexp = '\.jar$')
		if absolute:
			prePath = filelist.getAbsDir(env, path)
		else:
			prePath = os.path.join(prefixPath, path)
		clsPath = prePath
		for j in jarList:
			clsPath = clsPath + os.pathsep + os.path.join(prePath, j)
		return clsPath

	def createClassPath(self):
		classPath = ''
		if (self.additionalClassPath != None):
			classPath += self.additionalClassPath
		if (self.dependsOnProject != None):
			if (classPath != ''):
				classPath += os.pathsep
			classPath += self.dependsOnProject.getExternalClassPath()
		for cpd in self.classPathDirs:
			if (classPath != ''):
				classPath += os.pathsep
			classPath += self.createClassPathPart(cpd, absolute = True)
		return classPath

	def getExternalClassPath(self):
		extCP = self.createClassPath()
		if (self.binJarFile != None):
			extCP += os.pathsep + self.binJarFile
		return extCP

	def createSourcePath(self):
		sourcePath = ''
		for sd in self.sourceDirs:
			if (sourcePath != ''):
				sourcePath += os.pathsep
			sourcePath += filelist.getAbsDir(env, sd)
		for sd in self.sourceDirs_gen:
			if (sourcePath != ''):
				sourcePath += os.pathsep
			sourcePath += filelist.getAbsDir(env, sd)
		return sourcePath

	def doPrepareEnv(self):
		self.env['JAVACLASSPATH'] = self.createClassPath()
		self.env['JAVASOURCEPATH'] = self.createSourcePath()
		if (self.env['debug'] > 0):
			# Generate all java debug info
			env['JAVACFLAGS'] = '-g:lines,source,vars'
		else:
			# Generate only the default java debug info
			env['JAVACFLAGS'] = '-g:lines,source'
		return

	def createCompileCommand(self):
		cwdBak = os.getcwd()
		if (not os.path.exists(self.mainSrcDir)):
			os.makedirs(self.mainSrcDir)
		os.chdir(self.mainSrcDir)
		compile_cmd = self.env.Java(target = self.outDir, source = self.mainSrcDir, chdir = self.mainSrcDir)
		os.chdir(cwdBak)
		return compile_cmd

	def createDynamicCompileCommand(self):
		self.myDynamicCompileDummy = self.env.Command(self.name + '_compile_dummy', [], JavaProjCallback_dynamicCompile(self.name))
		self.env.Alias(self.name, self.myDynamicCompileDummy)
		return self.myDynamicCompileDummy

	def dynamicCompile_buildTime(self):
		sourceFiles = []
		for sd in self.mainSrcDirs:
			sourceFiles += self.doSourceGlob(sd)
		#compile_cmd = self.env.Java(target = self.outDir, source = sourceFiles, chdir = self.sourceDirs_gen[0])
		#compile_cmd = self.env.Java(target = self.outDir, source = sourceFiles)
		compile_cmd = self.env.Java(target = self.outDir, source = self.mainSrcDirs)
		#compile_cmd = self.env.Java(target = self.outDir, source = self.sourceDirs + self.sourceDirs_gen, chdir = self.sourceDirs_gen[0])
		self.env.Depends(self.name, compile_cmd)
		return

	def doSourceGlob(self, path):
		#sourceFiles = glob.glob(os.path.join(path, '**/*.java'))
		sourceFiles_rel = filelist.list_files_recursive(self.env, path, exclude_dirs = True, path_relative = True, include_regexp = '\.java$')
		sourceFiles = []
		for sfr in sourceFiles_rel:
			sourceFiles += [os.path.join(path, sfr)]
		return sourceFiles

	def packBinJar(self, manifestFile = None):
		#myBinJarEnv = self.env.Clone(JARCHDIR = self.outDir)
		#jarSource = [self.outDir]
		#if (manifestFile != None):
		#	jarSource += [manifestFile]
		#packBinJar_cmd = myBinJarEnv.Jar(target = self.binJarFile, source = jarSource)
		#return packBinJar_cmd
		self.binManifestFile = manifestFile
		self.namePackBin = self.name + '_pack_bin'
		self.binJarEnv = self.env.Clone()
		self.myPackBinDummy = self.binJarEnv.Command(self.name + '_packBin_dummy', [self.outDir], JavaProjCallback_packBin(self.name))
		self.env.Alias(self.namePackBin, self.myPackBinDummy)
		return self.myPackBinDummy

	def packBinJar_buildTime(self):
		self.packJar_buildTime(True)
		return

	def packSrcJar(self, manifestFile = None):
		self.srcManifestFile = manifestFile
		self.namePackSrc = self.name + '_pack_src'
		self.srcJarEnv = self.env.Clone()
		self.myPackSrcDummy = self.srcJarEnv.Command(self.name + '_packSrc_dummy', self.sourceDirs + self.sourceDirs_gen, JavaProjCallback_packSrc(self.name))
		self.env.Alias(self.namePackSrc, self.myPackSrcDummy)
		return self.myPackSrcDummy

	def packSrcJar_buildTime(self):
		self.packJar_buildTime(False)
		return

	def packJar_buildTime(self, binOrSrc):
		if (binOrSrc):
			allSourceDirs = [self.outDir]
			manifestFile = self.binManifestFile
			jarFile = self.binJarFile
		else:
			allSourceDirs = self.sourceDirs + self.sourceDirs_gen
			manifestFile = self.srcManifestFile
			jarFile = self.srcJarFile
		createOrUpdate = 'c'
		for sd in allSourceDirs:
			cmdLine = 'jar ' + createOrUpdate + 'f'
			if (createOrUpdate == 'c'):
				if (manifestFile != None):
					cmdLine += 'm'
			cmdLine += ' ' + jarFile
			if ((createOrUpdate == 'c') and (manifestFile != None)):
				cmdLine += ' ' + manifestFile
			cmdLine += ' -C ' + filelist.getAbsDir(env, sd) + ' .'
			if (createOrUpdate == 'c'):
				createOrUpdate = 'u'
			print cmdLine
			os.system(cmdLine)
		return

# These are "callbacks" that we set up as actions that Scons can call at build time.
def javaProjCallback_dynamicCompile(libName):
	if (not (libName in JavaProject.instances)):
		raise Exception("The library %s is not defined" % libName)
	JavaProject.instances[libName].dynamicCompile_buildTime()
	return
JavaProjCallback_dynamicCompile = SCons.Action.ActionFactory(javaProjCallback_dynamicCompile, lambda name: 'Compiling "%s"' % name)

# These are "callbacks" that we set up as actions that Scons can call at build time.
def javaProjCallback_packBin(libName):
	if (not (libName in JavaProject.instances)):
		raise Exception("The library %s is not defined" % libName)
	JavaProject.instances[libName].packBinJar_buildTime()
	return
JavaProjCallback_packBin = SCons.Action.ActionFactory(javaProjCallback_packBin, lambda name: 'Packing classes for "%s"' % name)

def javaProjCallback_packSrc(libName):
	if (not (libName in JavaProject.instances)):
		raise Exception("The library %s is not defined" % libName)
	JavaProject.instances[libName].packSrcJar_buildTime()
	return
JavaProjCallback_packSrc = SCons.Action.ActionFactory(javaProjCallback_packSrc, lambda name: 'Packing source for "%s"' % name)


################################################################################
### Setup the envs
################################################################################
# Make a copy of the build environment for the AIs, but remove libraries and add include path.
# TODO: make separate SConstructs for AIs
#ai_env = env.Clone(builddir=os.path.join(env['builddir'], 'AI'))
#ai_env['LIBS'] = []

ai_env.AppendUnique(CPPPATH = ['#rts/ExternalAI'])
ai_env.AppendUnique(LINKFLAGS = ['-lstdc++'])
ai_env.AppendUnique(CPPDEFINES = ['BUILDING_AI'])
# set sourcedir to '', which equals CWD
ai_env['sourcedir'] = ''
ai_env['builddir'] = filelist.getAbsDir(env, ai_env['builddir'])
if ai_env['platform'] == 'windows':
	ai_env.AppendUnique(LINKFLAGS = ['-Wl,--kill-at', '--add-stdcall-alias', '-mno-cygwin'])

#for d in filelist.list_directories(ai_env, filelist.getAbsDir(env, '#rts'), exclude_list=["crashrpt"]):
#	ai_env.BuildDir(os.path.join(ai_env['builddir'], 'Interfaces', d), d)
#	#print "d pj: %s" % os.path.join(ai_env['builddir'], 'Interfaces', d)
#	#print "d: %s" % d

aiinterface_env = create_sub_interface(ai_env, 'Interfaces')
aiinterface_env.AppendUnique(CPPDEFINES = ['BUILDING_AI_INTERFACE'])

skirmishai_env = create_sub_interface(ai_env, 'Skirmish')
skirmishai_env.AppendUnique(CPPDEFINES = ['BUILDING_SKIRMISH_AI'])


################################################################################
### Build AI Interface shared objects
################################################################################

# Store shared ai-interface objects, so newer SCons versions do not choke with:
# *** Two environments with different actions were specified for the same target
aiinterfaceobjs_main = create_shared_objects(aiinterface_env, filelist.get_shared_AIInterface_source(env), '-aiinterface')
aiinterfaceobjs_SharedLib = create_shared_objects(aiinterface_env, filelist.get_shared_AIInterface_source_SharedLib(env), '-aiinterface')
aiinterfaceobjs_CUtils = create_shared_objects(aiinterface_env, filelist.get_shared_Wrapper_source(ai_env, 'CUtils'), '-aiinterface')
aiinterfaceobjs_CUtilsStreflopped = create_shared_objects(aiinterface_env, filelist.get_shared_Wrapper_source(ai_env, 'CUtils'), '-aiinterface_withStreflop', ['USING_STREFLOP'])

# Build
aiinterfaces_exclude_list=['build']
aiinterfaces_needSharedLib_list=['C']
aiinterfaces_needStreflop_list=['Java']
aiinterfaces_needCUtils_list=['C', 'Java']

if (aiinterface_env['ai_interfaces'] == "native"):
	aiinterfaces_toBuild = ['C']
elif (aiinterface_env['ai_interfaces'] == "java"):
	aiinterfaces_toBuild = ['Java']
elif (aiinterface_env['ai_interfaces'] == "none"):
	aiinterfaces_toBuild = []
else:
	aiinterfaces_toBuild = filelist.list_AIInterfaces(aiinterface_env, exclude_list=aiinterfaces_exclude_list)

# These two will be filled here and used by Java Skirmish AIs further down
javaAiInterfaceProj = None

for baseName in aiinterfaces_toBuild:
	aiInterfaceVersion = fetch_aiInterface_version(baseName)
	print "Found AI Interface: " + baseName + " " + aiInterfaceVersion
	needSharedLib = baseName in aiinterfaces_needSharedLib_list
	needCUtils = baseName in aiinterfaces_needCUtils_list
	needStreflop = baseName in aiinterfaces_needStreflop_list
	myEnv = create_sub_interface(aiinterface_env, baseName)
	myEnv['installprefix'] = os.path.join(myEnv['installprefix'], aiInterfaceVersion)
	instList = []
	objs = []
	objs += aiinterfaceobjs_main
	if needSharedLib:
		objs += aiinterfaceobjs_SharedLib
	if needCUtils:
		myEnv.AppendUnique(CPPPATH = ['Wrappers'])
		if needStreflop:
			objs += aiinterfaceobjs_CUtilsStreflopped
		else:
			objs += aiinterfaceobjs_CUtils
	if needStreflop:
		myEnv.AppendUnique(CPPDEFINES=['USING_STREFLOP'])
		myEnv.AppendUnique(CPPPATH = ['#rts/lib/streflop'])
		myEnv.AppendUnique(LIBS = [streflop_lib])
	cwdPrev = os.getcwd()
	os.chdir(myEnv['sourcedir'])
	mySourceFiles = filelist.get_source(myEnv, '.', ignore_builddir=False)
	mySourceObjs = [myEnv.SharedObject(source=f, target=os.path.join(env['builddir'], f[0]+myEnv['SHARED_OBJ_EXT'])) for f in mySourceFiles]
	myObjs = objs + mySourceObjs
	os.chdir(cwdPrev)
	if baseName == 'Java':
		# generate class files
		springSrcDir = filelist.getAbsDir(env, '..')
		springInterfaceSrcDir = os.path.join(springSrcDir, 'rts', 'ExternalAI', 'Interface')
		interfaceJLibDir = os.path.join(myEnv['sourcedir'], 'data', 'jlib')
		interfaceJavaSrcDir = os.path.join(myEnv['sourcedir'], 'java', 'src')
		interfaceManifestFile = filelist.getAbsDir(env, os.path.join(interfaceJavaSrcDir, 'manifest.mf'))
		interfaceBinDir = os.path.join('AI', myEnv['sourcedir'], 'bin')
		awkScriptsDir = interfaceBinDir
		awkCommonScriptsDir = os.path.join(springSrcDir, 'AI', 'Wrappers', 'CUtils', 'bin')
		interfaceGeneratedJavaSrcDir = filelist.getAbsDir(env, os.path.join(myEnv['builddir'], 'generated-java-src'))
		interfaceTmpAllSrcDir = filelist.getAbsDir(env, os.path.join(myEnv['builddir'], 'tmp-all-src'))
		interfaceJavaClassesDir = filelist.getAbsDir(env, os.path.join(myEnv['builddir'], 'classes'))
		javaPkg = 'com/clan_sy/spring/ai'
		jarFileBase = 'AIInterface'
		jarFileBin = jarFileBase + '.jar'
		jarFileSrc = jarFileBase + '-src.jar'
		jarFileBin_build = os.path.join(myEnv['builddir'], jarFileBin)
		jarFileSrc_build = os.path.join(myEnv['builddir'], jarFileSrc)
		genSrcPkgDir = os.path.join(interfaceGeneratedJavaSrcDir, javaPkg)
		genSrcPkgDir = filelist.getAbsDir(env, genSrcPkgDir)

		awkVars = []
		awkVars += ['SPRING_SOURCE_DIR=' + filelist.getAbsDir(env, springSrcDir)]
		awkVars += ['INTERFACE_SOURCE_DIR=' + filelist.getAbsDir(env, interfaceJavaSrcDir)]
		awkVars += ['GENERATED_SOURCE_DIR=' + filelist.getAbsDir(env, interfaceGeneratedJavaSrcDir)]
		# gawk doesn't like backslashes on windows
		if os.name == 'nt':
			for i, e in enumerate(awkVars):
				awkVars[i] = e.replace('\\', '/')

		# wrapp the AI Events
		awkScripts = []
		awkScripts += ['jna_wrappEvents.awk']
		awkScripts += [os.path.join(awkCommonScriptsDir, 'common.awk')]
		awkScripts += [os.path.join(awkCommonScriptsDir, 'commonDoc.awk')]
		awkInputFile = os.path.join(springInterfaceSrcDir, 'AISEvents.h')
		awkOutput = [os.path.join(genSrcPkgDir, 'event', 'InitAIEvent.java')]
		wrapp_events = AWKExecutor(myEnv, 'wrapp_events', awkScripts, awkVars, awkInputFile, awkOutput, workDir = awkScriptsDir)
		wrapp_events_cmd = wrapp_events.createExecuteCommand()
		myEnv.AddPreAction(wrapp_events_cmd, ensureDirExists_action(os.path.join(genSrcPkgDir, 'event')))
		myEnv.AddPreAction(wrapp_events_cmd, ensureDirExists_action(os.path.join(genSrcPkgDir, 'oo')))

		# wrapp the Commands
		awkScripts = []
		awkScripts += ['jna_wrappCommands.awk']
		awkScripts += [os.path.join(awkCommonScriptsDir, 'common.awk')]
		awkScripts += [os.path.join(awkCommonScriptsDir, 'commonDoc.awk')]
		awkInputFile = os.path.join(springInterfaceSrcDir, 'AISCommands.h')
		awkOutput = [os.path.join(genSrcPkgDir, 'command', 'BuildUnitAICommand.java')]
		wrapp_commands = AWKExecutor(myEnv, 'wrapp_commands', awkScripts, awkVars, awkInputFile, awkOutput, workDir = awkScriptsDir)
		wrapp_commands_cmd = wrapp_commands.createExecuteCommand()
		myEnv.AddPreAction(wrapp_commands_cmd, ensureDirExists_action(os.path.join(genSrcPkgDir, 'command')))
		myEnv.AddPreAction(wrapp_commands_cmd, ensureDirExists_action(os.path.join(genSrcPkgDir, 'oo')))

		# wrapp the Callback
		awkScripts = []
		awkScripts += ['jna_wrappCallback.awk']
		awkScripts += [os.path.join(awkCommonScriptsDir, 'common.awk')]
		awkScripts += [os.path.join(awkCommonScriptsDir, 'commonDoc.awk')]
		awkInputFile = os.path.join(springInterfaceSrcDir, 'SSkirmishAICallback.h')
		awkOutput = [os.path.join(genSrcPkgDir, 'AICallback.java')]
		wrapp_callback = AWKExecutor(myEnv, 'wrapp_callback', awkScripts, awkVars, awkInputFile, awkOutput, workDir = awkScriptsDir)
		wrapp_callback_cmd = wrapp_callback.createExecuteCommand()
		myEnv.AddPreAction(wrapp_commands_cmd, ensureDirExists_action(os.path.join(genSrcPkgDir)))

		# wrapp the Callback in an OO layer
		awkScripts = []
		awkScripts += ['java_wrappCallbackOO.awk']
		awkScripts += [os.path.join(awkCommonScriptsDir, 'common.awk')]
		awkScripts += [os.path.join(awkCommonScriptsDir, 'commonDoc.awk')]
		awkScripts += [os.path.join(awkCommonScriptsDir, 'commonOOCallback.awk')]
		awkInputFile = os.path.join(genSrcPkgDir, 'AICallback.java')
		awkOutput = [
				os.path.join(genSrcPkgDir, 'oo', 'OOAICallback.java'),
				# if you add these, scons will delete them (WHYYYYYYYYYY!?!?!?!)
				#os.path.join(genSrcPkgDir, 'oo', 'OOAIFactory.java'),
				#os.path.join(genSrcPkgDir, 'oo', 'AbstractOOAI.java'),
				]
		wrapp_callbackOO = AWKExecutor(myEnv, 'wrapp_callbackOO', awkScripts, awkVars, awkInputFile, awkOutput, workDir = awkScriptsDir)
		wrapp_callbackOO_cmd = wrapp_callbackOO.createExecuteCommand()
		myEnv.AddPreAction(wrapp_callbackOO_cmd, ensureDirExists_action(os.path.join(genSrcPkgDir, 'oo')))

		wrapp_all = [wrapp_events_cmd, wrapp_commands_cmd, wrapp_callback_cmd, wrapp_callbackOO_cmd]


		# compile the Java part
		aiInterfaceProj = JavaProject(myEnv, 'aiInterfaceProj', [interfaceJLibDir], [filelist.getAbsDir(env, interfaceJavaSrcDir)], [filelist.getAbsDir(env, interfaceGeneratedJavaSrcDir)], interfaceJavaClassesDir, jarFileBin_build, jarFileSrc_build)
		javaAiInterfaceProj = aiInterfaceProj
		aiInterfaceProj_compile_cmd = aiInterfaceProj.createDynamicCompileCommand()
		myClasses = aiInterfaceProj_compile_cmd

		# pack the java binaries
		aiInterfaceProj_packBin_cmd = aiInterfaceProj.packBinJar(interfaceManifestFile)
		myJarBin = aiInterfaceProj_packBin_cmd

		# pack the java sources
		aiInterfaceProj_packSrc_cmd = aiInterfaceProj.packSrcJar()
		myJarSrc = aiInterfaceProj_packSrc_cmd

		allBuilds = [wrapp_all, myClasses, myJarBin, myJarSrc]
		Alias(baseName, allBuilds)
		Alias('AIInterfaces', allBuilds)
		Default(allBuilds)
		instList += [myEnv.Install(myEnv['installprefix'], jarFileBin_build)]
		instList += [myEnv.Install(os.path.join(myEnv['installprefix'], 'jlib'), jarFileSrc_build)]

	targetName = 'AIInterface'
	if env['platform'] != 'windows':
		targetName = 'lib' + targetName
	lib = myEnv.SharedLibrary(os.path.join(myEnv['builddir'], targetName), myObjs)
	Alias(baseName, lib)       # Allow e.g. `scons Java' to compile just that specific AI interface.
	Alias('AIInterfaces', lib) # Allow `scons AIInterfaces' to compile all AI interfaces.
	Default(lib)
	instList += [myEnv.Install(myEnv['installprefix'], lib)]
	if myEnv['strip']:
		myEnv.AddPostAction(lib, Action([['strip','$TARGET']]))

	# record data files (eg InterfaceInfo.lua or config files) for installation
	source_data_dir = os.path.join(myEnv['sourcedir'], 'data')
	install_dir_verbatim(myEnv, myEnv['installprefix'], source_data_dir, instList)

	Alias('install', instList)
	Alias('install-AIInterfaces', instList)
	Alias('install-' + baseName, instList)

################################################################################
### Build Skirmish AI shared objects
################################################################################
install_skirmishai_dir = os.path.join(skirmishai_env['installprefix'], skirmishai_env['datadir'], 'AI', 'Skirmish')

# store shared ai objects so newer scons versions don't choke with
# *** Two environments with different actions were specified for the same target
skirmishaiobjs_main = create_shared_objects(skirmishai_env, filelist.get_shared_skirmishAI_source(env), '-skirmishai')
skirmishaiobjs_mainCregged = create_shared_objects(skirmishai_env, filelist.get_shared_skirmishAI_source(env), '-skirmishai_creg', ['USING_CREG'])
skirmishaiobjs_creg = create_shared_objects(skirmishai_env, filelist.get_shared_skirmishAI_source_Creg(env), '-skirmishai_creg', ['USING_CREG'])
skirmishaiobjs_LegacyCpp = create_shared_objects(skirmishai_env, filelist.get_shared_skirmishAI_source_LegacyCpp(env), '-skirmishai')
skirmishaiobjs_LegacyCppCregged = create_shared_objects(skirmishai_env, filelist.get_shared_skirmishAI_source_LegacyCpp(env), '-skirmishai_creg', ['USING_CREG'])
skirmishaiobjs_CUtils = create_shared_objects(skirmishai_env, filelist.get_shared_Wrapper_source(ai_env, 'CUtils'), '-skirmishai')

# Build
skirmishai_exclude_list=['build', 'CSAI', 'TestABICAI', 'AbicWrappersTestAI']
skirmishai_isLegacyCpp_list=['AAI', 'KAIK', 'RAI', 'NullLegacyCppAI', 'KAI', 'NTai']
skirmishai_needCUtils_list=['AAI', 'KAIK', 'RAI', 'NullLegacyCppAI', 'KAI', 'NTai']
skirmishai_needCreg_list=['KAIK', 'KAI']
skirmishai_needBoost_list=['NTai']
skirmishai_isJava_list=['NullJavaAI', 'NullOOJavaAI']

skirmishais_toBuild = filelist.list_skirmishAIs(skirmishai_env, exclude_list=skirmishai_exclude_list, include_interfaces = aiinterfaces_toBuild)

for baseName in skirmishais_toBuild:
	aiVersion = fetch_skirmishAi_version(baseName)
	print "Found Skirmish AI: " + baseName + " " + aiVersion
	isLegacyCpp = baseName in skirmishai_isLegacyCpp_list
	needsCUtils = baseName in skirmishai_needCUtils_list
	useCreg = baseName in skirmishai_needCreg_list
	useBoost = baseName in skirmishai_needBoost_list
	isJava = baseName in skirmishai_isJava_list
	myEnv = create_sub_interface(skirmishai_env, baseName)
	myEnv['installprefix'] = os.path.join(myEnv['installprefix'], aiVersion)
	instList = []

	# create the library
	if isJava:
		javaSrc = os.path.join(myEnv['sourcedir'], 'src')
		javaClasses = os.path.join(myEnv['builddir'], 'classes')
		myJLibDir = os.path.join(myEnv['sourcedir'], 'data', 'jlib')
		myClassPathDirs = []
		if (os.path.exists(myJLibDir)):
			myClassPathDirs += [myJLibDir]
		manifestFile = filelist.getAbsDir(env, os.path.join(myEnv['sourcedir'], 'manifest.mf'))
		if (not os.path.exists(manifestFile)):
			manifestFile = None
		jarFileBase = 'SkirmishAI'
		jarFileBin = jarFileBase + '.jar'
		jarFileSrc = jarFileBase + '-src.jar'
		jarFileBin_build = os.path.join(myEnv['builddir'], jarFileBin)
		jarFileSrc_build = os.path.join(myEnv['builddir'], jarFileSrc)

		skirmishAiProj = JavaProject(myEnv, 'skirmishAiProj-' + baseName, myClassPathDirs, [filelist.getAbsDir(env, javaSrc)], [], javaClasses, jarFileBin_build, jarFileSrc_build, dependsOnProject = javaAiInterfaceProj)

		myCompile_cmd = skirmishAiProj.createDynamicCompileCommand()
		myBinJar_cmd = skirmishAiProj.packBinJar(manifestFile)
		mySrcJar_cmd = skirmishAiProj.packSrcJar()
		doAllJava = [myCompile_cmd, myBinJar_cmd, mySrcJar_cmd]

		Alias(baseName, doAllJava)
		Alias('SkirmishAI', doAllJava)
		Default(doAllJava)
		instList += [myEnv.Install(myEnv['installprefix'], jarFileBin_build)]
		instList += [myEnv.Install(os.path.join(myEnv['installprefix'], 'jlib'), jarFileSrc_build)]
	else:
		#if useBoost:
		#	# This code is stolen from config.py
		#	boost_thread = ['boost_thread']
		#	boost_regex  = ['boost_regex']
		#	if myEnv.Dictionary('CC').find('gcc') != -1: gcc = True
		#	else: gcc = False
		#	for boost in (boost_thread, boost_regex):
		#		l = boost[0]
		#		if gcc: boost = [l+'-gcc-mt', l+'-mt', l+'-gcc', l]
		#		else:   boost = [l+'-mt', l]
		#		myEnv.AppendUnique(LIBS = boost)
		#	#myEnv.AppendUnique(LIBS = boost_regex.libraries)
		if useCreg:
			myEnv.AppendUnique(CPPDEFINES = ['USING_CREG'])
		objs = []
		if useCreg:
			objs += skirmishaiobjs_mainCregged
			objs += skirmishaiobjs_creg
		else:
			objs += skirmishaiobjs_main
		if isLegacyCpp:
			myEnv.AppendUnique(CPPPATH = ['Wrappers'])
			if useCreg:
				objs += skirmishaiobjs_LegacyCppCregged
			else:
				objs += skirmishaiobjs_LegacyCpp
		if needsCUtils:
			myEnv.AppendUnique(CPPPATH = ['Wrappers'])
			objs += skirmishaiobjs_CUtils
		cwdPrev = os.getcwd()
		os.chdir(myEnv['sourcedir'])
		mySourceFiles = filelist.get_source(myEnv, '.', ignore_builddir=False)
		mySourceObjs = [myEnv.SharedObject(source=f, target=os.path.join(env['builddir'], f[0]+myEnv['SHARED_OBJ_EXT'])) for f in mySourceFiles]
		myObjs = objs + mySourceObjs
		os.chdir(cwdPrev)
		targetName = 'SkirmishAI'
		if env['platform'] != 'windows':
			targetName = 'lib' + targetName
		lib = myEnv.SharedLibrary(os.path.join(myEnv['builddir'], targetName), myObjs)
		Alias(baseName, lib)     # Allow e.g. `scons JCAI' to compile just a skirmish AI.
		Alias('SkirmishAI', lib) # Allow `scons SkirmishAI' to compile all skirmishAIs.
		Default(lib)
		instList += [myEnv.Install(myEnv['installprefix'], lib)]
		if myEnv['strip']:
			myEnv.AddPostAction(lib, Action([['strip','$TARGET']]))

	# record data files (eg AIInfo.lua or config files) for installation
	source_data_dir = os.path.join(myEnv['sourcedir'], 'data')
	install_dir_verbatim(myEnv, myEnv['installprefix'], source_data_dir, instList)

	# install everything from this AI
	Alias('install', instList)
	Alias('install-SkirmishAI', instList)
	Alias('install-' + baseName, instList)

